package org.eduapp.trains.crawler;

import java.util.Set;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.eduapp.trains.model.train.ModelTrain;
import org.eduapp.trains.model.train.ModelTrainSchedule;
import org.eduapp.trains.service.train.TrainService;
import org.eduapp.trains.utils.UtilsString;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import edu.uci.ics.crawler4j.crawler.Page;
import edu.uci.ics.crawler4j.crawler.WebCrawler;
import edu.uci.ics.crawler4j.url.WebURL;

/**
 * 
 * 数据更新源: http://search.huochepiao.com/chaxun/resultc.asp?txtCheCi=${trainNumber}
 * 
 * @author Administrator
 *
 */
public class TrainDataValidation extends WebCrawler {
	
	private final static Logger logger = Logger.getLogger(TrainDataValidation.class);
	
	/*
	 * should be removed after web integration.
	 * only for data crawling at first time.
	 */
	private ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"spring/application*.xml"});
	
	/*
	 * service for train model. 
	 * fetching and saving or updating
	 */
	private TrainService trainService = (TrainService)applicationContext.getBean("trainService");

	/*
	 * pattern to filter meta in the page
	 * 
	 */
	private Pattern filters = Pattern.compile(".*(\\.(css|js|bmp|gif|jpe?g"
			+ "|png|tiff?|mid|mp2|mp3|mp4" + "|wav|avi|mov|mpeg|ram|m4v|pdf"
			+ "|rm|smil|wmv|swf|wma|zip|rar|gz))$");

	public TrainDataValidation () {}
	
	public boolean shouldVisit(WebURL url) {
		if (url != null && url.getURL() != null) {
			String href = url.getURL().toLowerCase();
			
			if (filters.matcher(href).matches()) {
				return false;
			}
			
			if (href.toLowerCase().startsWith("http://search.huochepiao.com/chaxun/resultc.asp?txtcheci=")) {
				System.out.println("CheCi:" + href);
				return true;
			}
			
			if (href.toLowerCase().startsWith("http://www.huochepiao.com/lieche")) {
				System.out.println("LieChe:" + href);
				return true;
			}
			
		}
		return false;
	}
	
	public void visit(Page page) {
		
		String url = page.getWebURL().getURL();
		
		updateTrainData(page.getHTML(), url);
	}
	
	/**
	 * 
	 * @param text
	 * @return
	 */
	private String clearTags (String text) {
		if (UtilsString.isNotEmpty(text)) {
			text = text.replaceAll("<.*>", text);
		}
		return text;
	}
	
	/**
	 * 
	 * @param htmlContext
	 * @param urlContext
	 */
	private void updateTrainData (String htmlContent, String urlContext) {
		
		if (UtilsString.isNotEmpty(htmlContent) && 
				urlContext.toLowerCase().startsWith("http://search.huochepiao.com/chaxun/resultc.asp?txtcheci=")) {
			
			Document htmlDoc = Jsoup.parse(htmlContent);
			
			String trainNumber = 
				urlContext.toLowerCase().substring("http://search.huochepiao.com/chaxun/resultc.asp?txtcheci=".length());
			
			if (UtilsString.isNotEmpty(trainNumber)) {
				Elements eleTables = htmlDoc.select("center > table");
				
				try {
					ModelTrain trainModel = trainService.getByTrainNo(trainNumber);
					
					if (trainModel == null) {
						trainModel = new ModelTrain();
					}
					
					if (eleTables != null && eleTables.size() == 2) {
						
						//详细情况
						Element eleInfoTrain = eleTables.get(0);
						Elements eleTrs = eleInfoTrain.select("tr");
						
						if (eleTrs != null && eleTrs.size() > 3) {
							Elements r1    = eleTrs.get(0).select("td");
							String number  = clearTags(r1.get(2).text()); 		//车次
							String runtime = clearTags(r1.get(4).text());		//全程运行
							
							Elements r2         = eleTrs.get(1).select("td");
							String stationStart = clearTags(r2.get(1).text());	//始发站
							String stationEnd 	= clearTags(r2.get(3).text());	//终点站
							
							Elements r3         = eleTrs.get(2).select("td");
							String depatureTime = clearTags(r3.get(1).text());	//发车时间
							String arrivalTime  = clearTags(r3.get(3).text());	//到站时间
							
							Elements r4 = eleTrs.get(3).select("td");
							String trainType 	= clearTags(r4.get(1).text());	//类型
							String trainRange   = clearTags(r4.get(3).text());	//全程
							
							Elements r5 = eleTrs.get(4).select("td");
							String trainLastUpdate = clearTags(r5.get(0).text()).replace("最近更新", "").trim();
							
							if (trainModel != null && 
									trainModel.getTrainLastUpdate() != null && 
										trainLastUpdate.equals(trainModel.getTrainLastUpdate())) {
								logger.info("Train " + trainNumber + " stays latest, no need update....");
								return;
							}
							
							//列车基本信息
							trainModel.setTrainNumber(number);
							trainModel.setTrainRuntime(runtime);
							trainModel.setTrainStationStarting(stationStart);
							trainModel.setTrainStationTerminating(stationEnd);
							trainModel.setTrainTimeDeparture(depatureTime);
							trainModel.setTrainTimeArrival(arrivalTime);
							trainModel.setTrainType(trainType);
							trainModel.setTrainRange(trainRange);
							trainModel.setTrainLastUpdate(trainLastUpdate);
							
							//列车详细情况
							Element eleTbl = eleTables.get(1);
							Elements eleSchedules = eleTbl.select("tr");
							if (eleSchedules != null && eleSchedules.size() > 1) {
								
								Set<ModelTrainSchedule> schedules = trainModel.getTrainSchedules();
								
								for (int i = 1, size = eleSchedules.size(); i < size; i++) {
									Elements schedule = eleSchedules.get(i).select("td");
									
									ModelTrainSchedule modelSchedule = null;
									
									String trainScheduleSeq      = clearTags(schedule.get(1).text());			// 站次
									String trainScheduleStation  = clearTags(schedule.get(2).text());			// 站名
									String trainScheduleArrival  = clearTags(schedule.get(3).text());			// 到达时间 
									String trainScheduleDepature = clearTags(schedule.get(4).text());			// 开车时间
									String trainScheduleRuntime  = clearTags(schedule.get(5).text());			// 运行时间
									String trainScheduleRange    = clearTags(schedule.get(6).text());			// 里程
									
									if (schedules != null && schedules.size() > 0) {
										for (ModelTrainSchedule model : schedules) {
											if (model.getTrainStationSeq() == Integer.parseInt(trainScheduleSeq)) {
												modelSchedule = model;
												break;
											}
										}
									}
									
									if (modelSchedule == null) {
										modelSchedule = new ModelTrainSchedule();
									}
									
									// price initialization
									String trainPriceHardSeat 		  = "0";
									String trainPriceSoftSeat 		  = "0";
									String trainPriceHardBerthTop 	  = "0";
									String trainPriceHardBerthMiddle  = "0";
									String trainPriceHardBerthLow	  = "0";
									String trainPriceSoftLv2nd 		  = "0";
									String trainPriceSoftLv1st 		  = "0";
									String trainPriceSoftLvBest 	  = "0";
									String trainPriceSoftBerthTop 	  = "0";
									//String trainPriceSoftBerthMiddle  = "0";
									String trainPriceSoftBerthLow 	  = "0";
									
									// price summary
									String trainPriceHardBerth = null;
									String trainPriceSoftBerth = null;
									String trainPriceSoftLv	   = null;
									
									if (isHighWay(number)) {
										trainPriceHardBerth 	  = clearTags(schedule.get(7).text());	// 硬卧上/中/下 	
										trainPriceSoftBerth 	  = clearTags(schedule.get(8).text());	// 软卧上/下 	
										trainPriceSoftLv 		  = clearTags(schedule.get(9).text());	// 二等/一等软座
									} else {
										trainPriceHardSeat 		  = clearTags(schedule.get(7).text());	// 硬座
										trainPriceSoftSeat 		  = clearTags(schedule.get(8).text());	// 软座
										trainPriceHardBerth 	  = clearTags(schedule.get(9).text());	// 硬卧上/中/下 	
										trainPriceSoftBerth 	  = clearTags(schedule.get(10).text());	// 软卧上/下 	
									}
									
									if (UtilsString.isNotEmpty(trainPriceHardBerth) && 
											trainPriceHardBerth.split("/").length > 2) {
										String[] tmp = trainPriceHardBerth.split("/");
										trainPriceHardBerthTop 	  = tmp[0];
										trainPriceHardBerthMiddle = tmp[1];
										trainPriceHardBerthLow	  = tmp[2];
									}
									
									if (UtilsString.isNotEmpty(trainPriceSoftBerth) &&
											trainPriceSoftBerth.split("/").length > 1) {
										String[] tmp = trainPriceSoftBerth.split("/");
										trainPriceSoftBerthTop 	  = tmp[0];
										//trainPriceSoftBerthMiddle = tmp[1];
										trainPriceSoftBerthLow 	  = tmp[1];
									}
									
									if (UtilsString.isNotEmpty(trainPriceSoftLv)) {
										String[] tmp = trainPriceSoftLv.split("/");
										trainPriceSoftLv2nd 	  = tmp[0];
										trainPriceSoftLv1st 	  = tmp[1];
										if (trainPriceSoftLv.split("/").length > 2) {
											trainPriceSoftLvBest  = tmp[2];
										}
									}
									
									//ModelTrainSchedule modelSchedule = new ModelTrainSchedule();
									
									modelSchedule.setTrainStationSeq(Integer.parseInt(trainScheduleSeq));
									modelSchedule.setTrainStationName(trainScheduleStation);
									modelSchedule.setTrainArriveTime(trainScheduleArrival);
									modelSchedule.setTrainDepartureTime(trainScheduleDepature);
									modelSchedule.setTrainRuntime(trainScheduleRuntime);
									modelSchedule.setTrainRange(trainScheduleRange);
									
									modelSchedule.setTrainPriceHardSeat(Integer.parseInt(trainPriceHardSeat));
									modelSchedule.setTrainPriceHardBerthLow(Integer.parseInt(trainPriceHardBerthLow));
									modelSchedule.setTrainPriceHardBerthMiddle(Integer.parseInt(trainPriceHardBerthMiddle));
									modelSchedule.setTrainPriceHardBerthTop(Integer.parseInt(trainPriceHardBerthTop));
									
									modelSchedule.setTrainPriceSoftSeat(Integer.parseInt(trainPriceSoftSeat));
									modelSchedule.setTrainPriceSoftBerthLow(Integer.parseInt(trainPriceSoftBerthLow));
									//modelSchedule.setTrainPriceSoftBerthMiddle(trainPriceSoftBerthMiddle);
									modelSchedule.setTrainPriceSoftBerthTop(Integer.parseInt(trainPriceSoftBerthTop));
									
									modelSchedule.setTrainPriceSoftLv1st(Integer.parseInt(trainPriceSoftLv1st));
									modelSchedule.setTrainPriceSoftLv2nd(Integer.parseInt(trainPriceSoftLv2nd));
									modelSchedule.setTrainPriceSoftLvBest(Integer.parseInt(trainPriceSoftLvBest));
									
									modelSchedule.setTrainModel(trainModel);
									
									schedules.add(modelSchedule);
								}
								
								trainModel.setTrainSchedules(schedules);
							}
							
							trainService.saveOrUpdate(trainModel);
							
							logger.info("Train updated: " + trainNumber + "....");
							System.out.println("Crawler > Updated '" + trainNumber + "'...");
						}
					} else {
						if (trainModel != null) {
							trainService.remove(trainModel);
							logger.info("Train " + trainNumber + " not available....");
						}
					}
				} catch (Exception e) {
					logger.error("Train updated failed" + trainNumber + ", caused by " + e.getMessage() + "....");
					e.printStackTrace();
				}
			}
		}
		
		return;
	}
	
	/**
	 * highway against on the given train code
	 * @param trainCode
	 * @return
	 */
	private boolean isHighWay (String trainCode) {
		if (UtilsString.isNotEmpty(trainCode)) {
			return trainCode.toLowerCase().startsWith("c") || 
				trainCode.toLowerCase().startsWith("d") || trainCode.toLowerCase().startsWith("g");
		}
		return false;
	}

	public static Logger getLogger() {
		return logger;
	}
	
}
